//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2005
//
// Mini-map window.
//

# include "StdAfx.h"
# include "Resource.h"
# include "MiniMap.h"
# include "MapView.h"
# include "Map.h"
# include "Position.h"
# include "Scale.h"
# include "Settings.h"
# include "Menu.h"
# include "Wrappers.h"
# include "fast_floor.h"
# include "Globals.h"

HWND g_hMiniMapWnd;
long g_lMiniW, g_lMiniH;

static HBITMAP g_hMiniBmp;
static HDC g_hMiniMemDC;

static HPEN g_hSelPen1, g_hSelPen2;

LRESULT CALLBACK MiniMapWndProc (HWND _hWnd, UINT _uMsg, WPARAM _wParam, LPARAM _lParam);

bool InitMiniMap () {
	assert (g_hInst);
	assert (g_hMapWnd);

	WNDCLASSEX wcex_mm;
	::memset (& wcex_mm, 0, sizeof (wcex_mm));
	wcex_mm.cbSize		= sizeof (WNDCLASSEX); 
	wcex_mm.style		= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wcex_mm.lpfnWndProc	= (WNDPROC) MiniMapWndProc;
	wcex_mm.hInstance	= g_hInst;
	wcex_mm.hCursor		= g_hArrowCursor;
	wcex_mm.lpszClassName = "mapview_mm";
	::RegisterClassEx (& wcex_mm);

	g_hMiniMapWnd = ::CreateWindowEx (
		WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST,
		"mapview_mm", GetStr (IDS_MINI_MAP),
		WS_CHILDWINDOW | WS_CAPTION | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_SYSMENU |
		(GetSetting ("ShowMiniMap", true) ? WS_VISIBLE : 0),
		GetSetting ("MiniMapX", DWORD (0)), GetSetting ("MiniMapY", DWORD (0)), 1, 1,
		g_hMapWnd, NULL, g_hInst, NULL
	);
	if (! g_hMiniMapWnd)
		return false;

	g_hSelPen1 = ::CreatePen (PS_SOLID, 3, c_colBlack);
	g_hSelPen2 = ::CreatePen (PS_SOLID, 1, c_colLtGray);

	return true;
}

void DestroyMiniMap () {
	if (g_hMiniMemDC)
		::DeleteDC (g_hMiniMemDC);
	if (g_hMiniBmp)
		::DeleteObject (g_hMiniBmp);

	if (g_hSelPen1)
		::DeleteObject (g_hSelPen1);
	if (g_hSelPen2)
		::DeleteObject (g_hSelPen2);
}

void RenderMiniMap () {
	if (g_lMapHeight > g_lMapWidth) {
		g_lMiniW = 100;
		g_lMiniH = g_lMapWidth ? 100*g_lMapHeight/g_lMapWidth : 100;
	} else {
		g_lMiniW = g_lMapHeight ? 100*g_lMapWidth/g_lMapHeight : 100;
		g_lMiniH = 100;
	}

	if (g_hMiniMemDC)
		::DeleteDC (g_hMiniMemDC);
	if (g_hMiniBmp)
		::DeleteObject (g_hMiniBmp);
	const CClientDC hMapWndDC (g_hMapWnd);
	g_hMiniMemDC = ::CreateCompatibleDC (hMapWndDC);
	g_hMiniBmp = ::CreateCompatibleBitmap (hMapWndDC, g_lMiniW, g_lMiniH);
	::SelectObject (g_hMiniMemDC, g_hMiniBmp);

	long lW, lH;
	{
		CCritSec::CLock lock (g_cs);
		lW = fast_floor (.5f + g_map.rectBound.Width  ()*g_kX);
		lH = fast_floor (.5f + g_map.rectBound.Height ()*g_kY);
	}
	if (lW > 0 && lH > 0) {
		::SetStretchBltMode (g_hMiniMemDC, HALFTONE);
		::StretchBlt (
			g_hMiniMemDC,
			0, 0, g_lMiniW, g_lMiniH, g_hMemDC,
			(g_lWidth - lW)/2, (g_lHeight - lH)/2, lW, lH,
			SRCCOPY
		);
	}

	// Resize the mini-map.
	::SetWindowPos (g_hMiniMapWnd, NULL, 0, 0, g_lMiniW, g_lMiniH, SWP_NOMOVE | SWP_NOZORDER);
	RECT rectMini;
	::GetClientRect (g_hMiniMapWnd, & rectMini);
	::SetWindowPos (
		g_hMiniMapWnd, NULL, 0, 0,
		2*g_lMiniW - (rectMini.right - rectMini.left),
		2*g_lMiniH - (rectMini.bottom - rectMini.top),
		SWP_NOMOVE | SWP_NOZORDER
	);
}

void RedrawMiniMap () {
	::InvalidateRect (g_hMiniMapWnd, NULL, FALSE);
	::UpdateWindow (g_hMiniMapWnd);
}

static
LRESULT CALLBACK MiniMapWndProc (HWND _hWnd, UINT _uMsg, WPARAM _wParam, LPARAM _lParam) {
	switch (_uMsg) {
		case WM_PAINT: {
			PAINTSTRUCT ps;
			const HDC hDC = ::BeginPaint (_hWnd, & ps);

			// Draw the frame.
			RECT rectFrame;
			::GetClientRect (_hWnd, & rectFrame);
			::FrameRect (hDC, & rectFrame, g_hbrBlack);

			// Draw the mini-map.
			::BitBlt (hDC, 0, 0, g_lMiniW, g_lMiniH, g_hMiniMemDC, 0, 0, SRCCOPY);

			// Highlight the map visible area.
			if (g_lMapWidth > 0 && g_lMapHeight > 0) {
				::SelectObject (hDC, g_hbrNull);
				::SetBkMode (hDC, OPAQUE);
				::SetBkColor (hDC, c_colWhite);

				RECT rect;
				rect.left   = g_lX*g_lMiniW/g_lMapWidth - 2;
				rect.right  = rect.left + g_lWidth*g_lMiniW/g_lMapWidth + 4;
				rect.bottom = g_lMiniH - g_lY*g_lMiniH/g_lMapHeight + 2;
				rect.top    = rect.bottom - g_lHeight*g_lMiniH/g_lMapHeight - 4;

				::SelectObject (hDC, g_hSelPen1);
				::Rectangle (hDC, rect.left, rect.top, rect.right, rect.bottom);

				::SelectObject (hDC, g_hSelPen2);
				::Rectangle (hDC, rect.left, rect.top, rect.right, rect.bottom);
			}

			::EndPaint (_hWnd, & ps);
			break;
		}

		case WM_LBUTTONDOWN:
		case WM_RBUTTONDOWN:
			// Return keyboard input from label edit box.
			//if (g_bDialogMode)
				::SetFocus (g_hWnd);
			break;

		case WM_LBUTTONUP: {
			if (g_lMapWidth > g_lWidth || g_lMapHeight > g_lHeight) {
				const long lX = g_lMapWidth > g_lWidth ?
					(GET_X_LPARAM (_lParam) - 1)*g_lMapWidth/g_lMiniW - g_lWidth/2 :
					g_lX;
				const long lY = g_lMapHeight > g_lHeight ?
					(g_lMiniH - (GET_Y_LPARAM (_lParam) - 1))*g_lMapHeight/g_lMiniH - g_lHeight/2 :
					g_lY;

				SetPosition (lX, lY);
				Refresh ();
			}
			break;
		}

		case WM_MOVE: {
			RECT rect;
			::GetWindowRect (_hWnd, & rect);
			POINT p = {rect.left, rect.top};
			::ScreenToClient (g_hMapWnd, & p);
			if (p.x < 0)
				p.x = 0;
			if (p.y < 0)
				p.y = 0;

			SaveSetting ("MiniMapX", DWORD (p.x));
			SaveSetting ("MiniMapY", DWORD (p.y));
			break;
		}

		case WM_CLOSE:
			::ShowWindow (g_hMiniMapWnd, SW_HIDE);
			SaveSetting ("ShowMiniMap", false);
			UpdateMenu ();
			break;

		default:
			return ::DefWindowProc (_hWnd, _uMsg, _wParam, _lParam);
   }

   return FALSE;
}
